import Tablero
import Othello
import HUnit
import Maybe

-- evaluar t para correr todos los tests
t = runTestTT allTests

allTests = test [ 
	"othello" ~: testsOthello
	]
	
-- Test módulo Othello
juegoInicial = (J Blanco tableroInicial)
posiciones = [ (x,y::Int) | x<-['a'..'h'], y<-[1..8] ]
posValidasIniciales = [f_5,e_6,d_3,c_4]
todasLasPosMenos ps = filter (not.(flip elem ps)) posiciones
jugador (J color _) = color
tablero (J _ tablero) = tablero
jugadas = [M ('c',4),M ('c',3),M ('c',2),M ('b',2),M ('a',2),M ('a',1),M ('d',3),M ('a',3),M ('b',3),M ('b',4),M ('a',4),M ('a',5),M ('b',1),M ('c',1),M ('e',6),M ('b',5),M ('a',6),M ('a',7),Paso,M ('b',6),Paso,M ('c',5),Paso,M ('e',2),M ('d',2),M ('e',1),M ('d',1),M ('e',3),M ('f',1),M ('d',6),M ('c',6),M ('c',7),M ('b',7),M ('b',8),M ('a',8),M ('c',8),M ('d',8),M ('d',7),M ('f',3),M ('e',7),M ('e',8),M ('f',2),M ('g',3),M ('f',4),M ('f',5),M ('f',6),M ('f',7),M ('f',8),M ('g',8),M ('g',1),Paso,M ('g',2),M ('h',1),M ('g',4),M ('g',5),M ('g',6),M ('g',7),M ('h',7),M ('h',2),M ('h',3),M ('h',4),M ('h',5),M ('h',6),M ('h',8),Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso,Paso]
juegoCompleto = fromJust (jugarAlternado jugadas (J Blanco tableroInicial))

jugarAlternado :: [Jugada] -> Juego -> Maybe Juego
jugarAlternado lista juego = foldl (\j m -> jugar m (fromJust j)) (Just juego) lista 

altura :: Arbol a -> Int
altura = foldArbol (\x rs->if (null rs) then 1 else 1+(maxlista rs))
	where maxlista = foldr1 max

testsOthello = test [
	-- test jugadaValida
	True  ~=? all (\p -> jugadaValida (M p) juegoInicial) posValidasIniciales,
	False ~=? any (\p -> jugadaValida (M p) juegoInicial) (todasLasPosMenos posValidasIniciales),
	-- test jugar
	True  ~=? all isNothing (map ((flip jugar juegoInicial).M) (todasLasPosMenos posValidasIniciales)),
	True  ~=? all isJust    (map ((flip jugar juegoInicial).M) posValidasIniciales),
	-- test jugar: cambio de jugador
	_N    ~=? jugador (fromJust (jugar (M f_5) juegoInicial)),
	_B    ~=? jugador (fromJust (jugar (M f_6) (fromJust (jugar (M f_5) juegoInicial)) )),
	-- test jugar: ficha puesta
	Just _B ~=? contenido f_5 (tablero (fromJust (jugar (M f_5) juegoInicial))),
	Just _N ~=? contenido f_6 (tablero (fromJust (jugar (M f_6) (fromJust (jugar (M f_5) juegoInicial)) ))),
	-- test jugadas posibles
	posValidasIniciales ~~? map (\(M p,_) -> p) (jugadasPosibles juegoInicial),
	Paso ~=? fst (head (jugadasPosibles juegoCompleto)),
	-- test ganador
	Just _N ~=? ganador juegoCompleto,
	Just _B ~=? ganador (J _B (invertirTodas posiciones (tablero juegoCompleto))),
	Nothing ~=? ganador (J _B tableroInicial),
	-- test podar
	2 ~=? altura (podar 2 (arbolDeJugadas juegoInicial)),
	3 ~=? altura (podar 3 (arbolDeJugadas juegoInicial)),
	-- test valuación
	-1.0 ~=? valuacionOthello juegoCompleto,
	1.0  ~=? valuacionOthello (J _B (invertirTodas posiciones (tablero juegoCompleto))),
	0.0  ~=? valuacionOthello juegoInicial
	]

-- idem ~=? pero sin importar el orden
(~~?) :: (Ord a, Eq a, Show a) => [a] -> [a] -> Test
expected ~~? actual = (sort expected) ~=? (sort actual)
	where
		sort = foldl (\r e -> push r e) []
		push r e = (filter (e<=) r) ++ [e] ++ (filter (e>) r)

(~~) :: (Ord a, Eq a, Show a) => [a] -> [a] -> Bool
expected ~~ actual = (sort expected) == (sort actual)
	where
		sort = foldl (\r e -> push r e) []
		push r e = (filter (e<=) r) ++ [e] ++ (filter (e>) r)

-- constantes para que los tests sean más legibles		
_N = Negro
_B = Blanco
a_1 = ('a',1::Int)
b_1 = ('b',1::Int)
c_1 = ('c',1::Int)
d_1 = ('d',1::Int)
e_1 = ('e',1::Int)
f_1 = ('f',1::Int)
g_1 = ('g',1::Int)
h_1 = ('h',1::Int)

a_2 = ('a',2::Int)
b_2 = ('b',2::Int)
c_2 = ('c',2::Int)
d_2 = ('d',2::Int)
e_2 = ('e',2::Int)
f_2 = ('f',2::Int)
g_2 = ('g',2::Int)
h_2 = ('h',2::Int)

a_3 = ('a',3::Int)
b_3 = ('b',3::Int)
c_3 = ('c',3::Int)
d_3 = ('d',3::Int)
e_3 = ('e',3::Int)
f_3 = ('f',3::Int)
g_3 = ('g',3::Int)
h_3 = ('h',3::Int)

a_4 = ('a',4::Int)
b_4 = ('b',4::Int)
c_4 = ('c',4::Int)
d_4 = ('d',4::Int)
e_4 = ('e',4::Int)
f_4 = ('f',4::Int)
g_4 = ('g',4::Int)
h_4 = ('h',4::Int)

a_5 = ('a',5::Int)
b_5 = ('b',5::Int)
c_5 = ('c',5::Int)
d_5 = ('d',5::Int)
e_5 = ('e',5::Int)
f_5 = ('f',5::Int)
g_5 = ('g',5::Int)
h_5 = ('h',5::Int)

a_6 = ('a',6::Int)
b_6 = ('b',6::Int)
c_6 = ('c',6::Int)
d_6 = ('d',6::Int)
e_6 = ('e',6::Int)
f_6 = ('f',6::Int)
g_6 = ('g',6::Int)
h_6 = ('h',6::Int)

a_7 = ('a',7::Int)
b_7 = ('b',7::Int)
c_7 = ('c',7::Int)
d_7 = ('d',7::Int)
e_7 = ('e',7::Int)
f_7 = ('f',7::Int)
g_7 = ('g',7::Int)
h_7 = ('h',7::Int)

a_8 = ('a',8::Int)
b_8 = ('b',8::Int)
c_8 = ('c',8::Int)
d_8 = ('d',8::Int)
e_8 = ('e',8::Int)
f_8 = ('f',8::Int)
g_8 = ('g',8::Int)
h_8 = ('h',8::Int)
